package com.managed.bean;

import com.marlboroug.articles.ArticlesManagerRemote;
import com.marlboroug.articlesordered.ArticlesOrderedManagerRemote;
import com.marlboroug.entity.Articles;
import com.marlboroug.entity.Articlesbyauthors;
import com.marlboroug.entity.Articlesordered;
import com.marlboroug.entity.Orders;
import com.marlboroug.entity.Users;
import com.util.CommandResult;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import util.ShoppingCartItem;

/**
 * Composant pour la gestion du panier.
 *
 * @author Cyril Schumacher
 */
@ManagedBean(name = "shoppingCart")
@SessionScoped
public class ShoppingCartBean implements Serializable {
    // <editor-fold defaultstate="collapsed" desc="Constantes.">
    /**
     * Adresse URL relative de la page du panier.
     */
    private final static String _SHOPPING_INDEX_URL = "/faces/shopping/index.xhtml";
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Membres.">
    /**
     * Gestion des commandes.
     */
    @EJB
    private ArticlesOrderedManagerRemote _articlesOrderedManager;
    /**
     * Gestion des articles.
     */
    @EJB
    private ArticlesManagerRemote _articlesManager;
    /**
     * Panier (identifiant de l'article / quantité).
     */
    private List<ShoppingCartItem> _items;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Accesseurs/Mutateurs.">
    /**
     * Obtient le panier.
     *
     * @return Retourne le panier.
     */
    public List<ShoppingCartItem> getItems() {
        return _items;
    }

    /**
     * Obtient le nombre de livre dans le panier.
     *
     * @return Retourne le nombre.
     */
    public Integer getItemsNumber() {
        return _items.size();
    }

    /**
     * Obtient le prix total du panier.
     *
     * @return Retourne le montant.
     */
    public Double getSubtotal() {
        double subtotal = 0;
        for (ShoppingCartItem item : _items) {
            subtotal += item.getQuantity() * item.getArticle().getIdArticles().getPrice();
        }

        return floor(subtotal, 2);
    }

    /**
     * Obtient le prix total, avec la prise en compte de la TVA, du panier.
     *
     * @return Retourne le montant.
     */
    public Double getSubtotalTTC() {
        double subtotal = getSubtotal();
        return floor(subtotal + (subtotal * (19.6 / 100)), 2);
    }

    /**
     * Obtient une valeur indiquant si le panier contient des livres.
     *
     * @return Retourne TRUE ou FALSE.
     */
    public Boolean hasItems() {
        return _items.size() > 0;
    }

    /**
     * Redirige l'utilisateur si celui-ci n'a aucun livre dans le panier
     *
     * @throws java.io.IOException
     */
    public void hasNotItems() throws IOException {
        if (hasItems() == false) {
            ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
            externalContext.redirect(externalContext.getRequestContextPath() + _SHOPPING_INDEX_URL);
        }

    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Méthodes.">
    // <editor-fold defaultstate="collapsed" desc="Privées.">
    /**
     * Supprime les articles ayant une quantité de zéro.
     *
     * @param item Livre.
     * @return Retourne TRUE si le livre a été supprimé, FALSE sinon.
     */
    private Boolean _deleteArticlesWithZeroQuantity(ShoppingCartItem item) {
        if (item.getQuantity() <= 0) {
            _items.remove(item);
            return true;
        }

        return false;
    }

    /**
     * Vérifie si les quantités des articles.
     */
    private void _checkQuantityArticles() {
        for (Articles articles : _articlesManager.findAll()) {
            if (articles.getQuantityStock() == 0) {
                articles.setState(4);

                _articlesManager.update(articles);
            }
        }
    }

    /**
     * Arrondi un nombre décimal à n chiffres.
     *
     * @param a Valeur.
     * @param n Nombre après la virgule.
     * @return Retourne le nombre arrondi.
     */
    private static double floor(double a, int n) {
        double p = Math.pow(10.0, n);
        return Math.floor((a * p) + 0.5) / p;
    }
    // </editor-fold>

    /**
     * Ajoute un livre dans le panier.
     *
     * @param article Article.
     * @return Retourne "success" si l'utilisateur est authentifié, "failure"
     * sinon.
     */
    public String addArticles(Articlesbyauthors article) {
        // Créer un article, par défaut.
        ShoppingCartItem item = new ShoppingCartItem(article, 1);
        // Récupère l'identifiant de l'article.
        Long articleId = article.getIdArticles().getIDarticles();

        // Vérifie si le livre n'est pas déjà dans le panier.
        for (int i = 0; i < _items.size(); i++) {
            // Récupère l'article.
            ShoppingCartItem itemInCart = _items.get(i);

            // Si il est déjà dans le panier, on en ajoute un en plus.
            Long articleItemInCart = itemInCart.getArticle().getIdArticles().getIDarticles();
            if (articleItemInCart.equals(articleId)) {
                item = itemInCart;
                int quantity = item.getQuantity();
                item.setQuantity(++quantity);

                // Vérifie si la quantité ne dépasse pas la quantité du stock.
                if (item.getArticle().getIdArticles().getQuantityStock() < item.getQuantity()) {
                    item.setQuantity(item.getArticle().getIdArticles().getQuantityStock());
                }

                // Sauvegarde et termine l'exécution de la méthode.
                _items.set(i, item);
                return CommandResult.SUCCESS.toString();
            }
        }

        // Ajoute le nouvel élément.
        _items.add(item);
        return CommandResult.SUCCESS.toString();
    }

    /**
     * Met à jour un article.
     *
     * @param item Article.
     */
    public void update(ShoppingCartItem item) {
        int quantityLimit = item.getArticle().getIdArticles().getQuantityStock();
        
        // Supprime le livre si celui-ci a une quantité égale à 0.
        if (_deleteArticlesWithZeroQuantity(item) == false) {
            if (item.getQuantity() <= quantityLimit) {
                // Récupère l'identifiant de l'article.
                Long itemId = item.getArticle().getIdArticles().getIDarticles();
                for (int i = 0; i < _items.size(); i++) {
                    // Récupère l'article ainsi que son identifiant.
                    ShoppingCartItem itemInCart = _items.get(i);
                    Long articleItemInCart = itemInCart.getArticle().getIdArticles().getIDarticles();

                    if (articleItemInCart.equals(itemId)) {
                        _items.set(i, item);
                    }
                }
            } else {
                item.setQuantity(quantityLimit);
                for (int i = 0; i < _items.size(); i++) {
                    // Récupère l'article ainsi que son identifiant.
                    ShoppingCartItem itemInCart = _items.get(i);
                    Long articleItemInCart = itemInCart.getArticle().getIdArticles().getIDarticles();

                    // Récupère l'identifiant de l'article.
                    Long itemId = item.getArticle().getIdArticles().getIDarticles();
                    if (articleItemInCart.equals(itemId)) {
                        item.setQuantity(item.getArticle().getIdArticles().getQuantityStock());
                        _items.set(i, item);
                    }
                }
                
                FacesContext.getCurrentInstance().addMessage("id", new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        "Vous avez atteint le nombre de livre. Nombre de livre disponible en stock: " + item.getArticle().getIdArticles().getQuantityStock() + ".", null));
            }
        }
    }

    /**
     * Effectue le paiement.
     *
     * @param user Informations de l'utilisateur.
     * @return Retourne "success" si l'utilisateur est authentifié, "failure"
     * sinon.
     */
    public String buy(Users user) {
        List<Articlesordered> itemsForOrders = new ArrayList<>();

        // Créer une commande selon les informations de l'utilisateur
        // et le montant H.T. et T.T.C.
        Orders order = new Orders();
        order.setIdUsers(user);

        for (ShoppingCartItem item : _items) {
            // Ajoute le prix total T.T.C et H.T.
            order.setNetAmount(getSubtotal().floatValue());
            order.setGrossAmount(getSubtotalTTC().floatValue());

            // Ajoute l'article à la commande.
            Articlesordered forOrder = new Articlesordered();
            forOrder.setIdOrders(order);
            forOrder.setQuantityArticle(item.getQuantity());
            forOrder.setIdArticles(item.getArticle().getIdArticles());

            itemsForOrders.add(forOrder);
        }

        if (_articlesOrderedManager.OrderPlaced(itemsForOrders)) {
            _checkQuantityArticles();
            // Si la commande s'est bien effectuée, on initialise le panier.
            _items.clear();
            return CommandResult.SUCCESS.toString();
        }
        
        return CommandResult.FAILURE.toString();
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Initialisation de la classe.">
    /**
     * Initialise la classe après l'injection de dépendance.
     *
     * @see javax.annotation.PostConstruct
     */
    @PostConstruct
    public void initialize() {
        if (_items == null) {
            _items = new ArrayList<>();
        }

    }
    // </editor-fold>
}
